#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <iostream>
#include <netinet/in.h>
#include <vector>
using namespace std;

enum CMD{
	CMD_LOGIN,
	CMD_LOGIN_RESULT,
	CMD_LOGOUT,
	CMD_LOGOUT_RESULT,
	CMD_ERROR
};

struct DataHeader
{
	short datalength;
	short cmd;
};


struct Login:public DataHeader
{
	Login(){
		datalength=sizeof(Login);
		cmd=CMD_LOGIN;
	}
	char userName[32];
	char password[32];
};
struct LoginResult:public DataHeader
{
	LoginResult(){
		datalength=sizeof(LoginResult);
		cmd=CMD_LOGIN_RESULT;
		result=1;
	}
	int result;
};

struct Logout:public DataHeader
{
	Logout(){
		datalength=sizeof(Logout);
		cmd=CMD_LOGOUT;
	}
	char userName[32];
};
struct LogoutResult:public DataHeader
{
	LogoutResult(){
		datalength=sizeof(LogoutResult);
		cmd=CMD_LOGOUT_RESULT;
		result=1;
	}
	int result;
};

vector<__socket_type> g_clients;

int processor(__socket_type recvClientSocket){
	char szRecv[1024]={};
    	    int reLen = recv(recvClientSocket, szRecv, sizeof(DataHeader), 0);
			DataHeader* header = (DataHeader*)szRecv;
    	    if (-1 == reLen) 
			{
				cout << "Server failed to receive." << endl;
				return -1;
			}
			
	switch (header->cmd)
	{
		case CMD_LOGIN:{
			recv(recvClientSocket, szRecv+sizeof(DataHeader), header->datalength-sizeof(DataHeader), 0);
			Login* login=(Login*)szRecv;
			cout<<"Receive CMD: CMD_LOGIN, message length is "<<login->datalength<<" .User is "<<login->userName<<", password is "<<login->password<<"."<<endl;
			//username & password not judge;******************************
			LoginResult ret;
			send(recvClientSocket, (char*)&ret, sizeof(LoginResult), 0);
		}
		break;
		case CMD_LOGOUT:{
			recv(recvClientSocket, szRecv+sizeof(DataHeader), header->datalength-sizeof(DataHeader), 0);
			Logout* logout=(Logout*)szRecv;
			cout<<"Receive CMD: CMD_LOGOUT, message length is "<<logout->datalength<<" .User is "<<logout->userName<<"."<<endl;
			LogoutResult ret;
			send(recvClientSocket, (char*)&ret, sizeof(LogoutResult), 0);
		}
		break;
		default:
			header->cmd=CMD_ERROR;
			header->datalength=0;
			send(recvClientSocket, (char*)&header, sizeof(DataHeader), 0);
		break;
	}
}

int main(){
    //创建套接字
    int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(serv_sock==-1)
	{
		cout<<"Socket build failed."<<endl;
	}

    //将套接字和IP、端口绑定
    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));  //每个字节都用0填充
    serv_addr.sin_family = AF_INET;  //使用IPv4地址
    serv_addr.sin_addr.s_addr = inet_addr("192.168.30.128");  //具体的IP地址
    serv_addr.sin_port = htons(1234);  //端口
    const int on = 1;
    setsockopt(serv_sock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));//设置使bind在重启后不会出现（linux网络编程/tcp_server中server bind失败原因的）
    int bRes=bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
    if (-1 == bRes) 
	{
		cout << "Failed to bind." << endl;
	}
    else 
	{
		cout << "Binding success" << endl;
	}

    //进入监听状态，等待用户发起请求
    int lRes=listen(serv_sock, 20);
    if (-1 == lRes) 
	{
		cout << "Failed to listen." << endl;
	}
    else 
	{
		cout << "Server:Listenning..." << endl;
	}
	
	while(true){
		fd_set fdRead,fdWrite,fdException;
		FD_ZERO(&fdRead);
		FD_ZERO(&fdWrite);
		FD_ZERO(&fdException);

		FD_SET(serv_sock,&fdRead);
		FD_SET(serv_sock,&fdWrite);
		FD_SET(serv_sock,&fdException);

		for(int n = (int)g_clients.size()-1 ; n >= 0 ; n--){
			FD_SET(g_clients[n],&fdRead);
		}
		timeval t={0,0};
		int ret = select(serv_sock+1,&fdRead,&fdWrite,&fdException,&t);
		if(ret<0){
			cout<<"select ERROR!"<<endl;
			break;
		}
		if(FD_ISSET(serv_sock,&fdRead)){
			FD_CLR(serv_sock,&fdRead);
			struct sockaddr_in clnt_addr;
			socklen_t clnt_addr_size = sizeof(clnt_addr);
			int recvClientSocket = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
			if(recvClientSocket==-1)
				cout<<"Serve receive no client."<<endl;
			g_clients.push_back(recvClientSocket);
			cout<<"New client accepted , IP is "<<inet_ntoa(clnt_addr.sin_addr)<<endl;
		}
		for(size_t n=0;n<fdRead.fd_count;n++){
			if(-1 == processor(fdRead.fd_array[n])){
				auto iter=find(g_clients.begin(),g_clients.end(),fdRead.fd_array[n]);
				if(iter!=g_clients.end()){
					g_clients.erase(iter);
				}
			}
		}	
	}

    //关闭套接字
	for(size_t n=g_clients.size()-1;n>=0;n--){
		close(g_clients[n]);
	}
    close(serv_sock);
	cout<<"Server socket closed!"<<endl;

    return 0;
}
